﻿using System;
using SCG = System.Collections.Generic;

using Nemerle;
using Nemerle.Assertions;
using Nemerle.Compiler;
using Nemerle.Collections;
using Nemerle.Utility;
using PT = Nemerle.Compiler.Parsetree;
using TT = Nemerle.Compiler.Typedtree;

namespace Nemerle.OpenCL {

    macro CheckError(func : expr)
        syntax("chk_err", func)
    {
        match(func) {
            | PT.PExpr.Call(func, args) => CheckErrorImpl.MakeCheckError(func, NList.ToList(args))
            | _ => Message.FatalError("CheckError macro can be applied only to function calls.")
        }
    }

    macro CheckErrorTail(func : expr)
        syntax("chk_err_t", func)
    {
        match(func) {
            | PT.PExpr.Call(func, args) => CheckErrorImpl.MakeCheckErrorTail(func, NList.ToList(args))
            | _ => Message.FatalError("CheckErrorTail macro can be applied only to function calls.")
        }
    }

    module CheckErrorImpl { 

        private GetFunctionName(func : PT.PExpr) : string {
            | <[ $_.$func ]> => func.ToString()
            | _ => func.ToString();
        }

        public MakeCheckError(func : PT.PExpr, args : list[PT.PExpr]) : PT.PExpr {
            def expr = <[ $func(..$args) ]>;
            <[ 
                match($expr) {
                    | GASS.OpenCL.CLError.Success => ();
                    | error => throw OpenCLException(error, $(GetFunctionName(func) : string));
                }
            ]>
        }

        public MakeCheckErrorTail(func : PT.PExpr, args : list[PT.PExpr]) : PT.PExpr {
            def args = args + [ <[ ref status ]> ];
            def expr = <[ $func(..$args) ]>;
            <[
                mutable status;
                def result = $expr;
                match(status) {
                    | GASS.OpenCL.CLError.Success => result
                    | error => throw OpenCLException(error, $(GetFunctionName(func) : string));
                }
            ]>
        }

    }

}